package api

import (
	"bbs-go/pkg/config"
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"net"
	"os/exec"
	"regexp"
	"runtime"
	"strings"

	"github.com/ZYallers/gocrypt/aes"
	"github.com/kataras/iris/v12"
	"github.com/mlogclub/simple/common/jsons"
	"github.com/mlogclub/simple/web"
	"github.com/sirupsen/logrus"
)

type SignController struct {
	Ctx iris.Context
}

func (c *SignController) PostCreate() *web.JsonResult {
	params := c.Ctx.FormValue("params")

	reqMap := make(map[string]string)
	reqMap["params"] = params
	reqMap["mac"] = GetLocalMac()
	reqMap["serialNumber"] = GetSerialNumber()

	src := []byte(jsons.ToJsonStr(reqMap))

	//pubByte1, _ := os.ReadFile("public.key")
	//logrus.Info(string(pubByte1))
	//logrus.Info(config.Instance.Key)

	publicKey, _ := AesDecrypt(config.Instance.Key)
	pubByte, _ := base64.RawStdEncoding.DecodeString(publicKey)
	block := &pem.Block{
		Type:  "RSA PUBLIC KEY",
		Bytes: pubByte,
	}
	dst, _ := RsaEncryptBlock(src, pem.EncodeToMemory(block))

	// priByte, _ := os.ReadFile("./private.key")
	// dst, _ := openssl.RSASign(src, priByte, crypto.SHA256)
	// logrus.Info("Sign: ", base64.RawStdEncoding.EncodeToString(dst))

	resMap := make(map[string]string)
	resMap["ciphertext"] = base64.RawStdEncoding.EncodeToString(dst)
	code, _ := AesDecrypt(config.Instance.Code)
	resMap["code"] = code
	return web.JsonData(resMap)
}

func AesDecrypt(src string) (str string, err error) {
	aesKey := config.Instance.Salt
	aes.CbcCrypto.SetAesKey(aesKey)
	iv := []byte("0000000000000000")
	decByte, _ := aes.CbcCrypto.Decode(src)
	b, err := aes.CbcCrypto.Decrypt([]byte(decByte), iv)
	if err != nil {
		return "", err
	}
	logrus.Info("aes decrypt: ", aes.CbcCrypto.String(b))
	return aes.CbcCrypto.String(b), nil
}

/*
*
公钥加密-分段
*/
func RsaEncryptBlock(src, publicKeyByte []byte) (bytesEncrypt []byte, err error) {
	block, _ := pem.Decode(publicKeyByte)
	if block == nil {
		return
	}

	// x509 parse
	publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	publicKey, ok := publicKeyInterface.(*rsa.PublicKey)
	if !ok {
		return
	}

	keySize, srcSize := publicKey.Size(), len(src)
	//logrus.Info("密钥长度：", keySize, "\t明文长度：\t", srcSize)
	//单次加密的长度需要减掉padding的长度，PKCS1为11
	offSet, once := 0, keySize-11
	buffer := bytes.Buffer{}
	for offSet < srcSize {
		endIndex := offSet + once
		if endIndex > srcSize {
			endIndex = srcSize
		}
		// 加密一部分
		bytesOnce, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, src[offSet:endIndex])
		if err != nil {
			return nil, err
		}
		buffer.Write(bytesOnce)
		offSet = endIndex
	}
	bytesEncrypt = buffer.Bytes()
	return
}

func RsaGenKey(bits int) (privateKey string, publicKey string, err error) {
	rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, bits)

	if err != nil {
		return privateKey, publicKey, err
	}

	privateKeyBytes := x509.MarshalPKCS1PrivateKey(rsaPrivateKey)
	publicKeyBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivateKey.PublicKey)

	if err != nil {
		return privateKey, publicKey, err
	}

	privateBlock := &pem.Block{
		Type:  "private key",
		Bytes: privateKeyBytes,
	}
	publicBlock := &pem.Block{
		Type:  "public key",
		Bytes: publicKeyBytes,
	}

	// 将私钥/公钥写入内存并base64编码
	privateKey = base64.URLEncoding.EncodeToString(pem.EncodeToMemory(privateBlock))
	publicKey = base64.URLEncoding.EncodeToString(pem.EncodeToMemory(publicBlock))

	return privateKey, publicKey, nil

}

func (c *SignController) GetAgent() *web.JsonResult {
	return web.JsonData("ok")
}

func (c *SignController) GetInfo() *web.JsonResult {
	resMap := make(map[string]string)
	code, _ := AesDecrypt(config.Instance.Code)
	resMap["code"] = code
	resMap["mac"] = GetLocalMac()
	resMap["serialNumber"] = GetSerialNumber()
	return web.JsonData(resMap)
}

func GetLocalMac() (mac string) {
	// 获取本机的MAC地址
	interfaces, err := net.Interfaces()
	if err != nil {
		panic("Poor soul, here is what you got: " + err.Error())
	}
	var rs string = ""
	for _, inter := range interfaces {
		//logrus.Info(inter.Name)
		mac := inter.HardwareAddr //获取本机MAC地址
		if mac != nil {
			//logrus.Info("MAC =====111 ", mac)
			rs = mac.String()
		}
	}
	//logrus.Info("MAC = ", rs)
	return rs
}

func GetSerialNumber() string {
	sysType := runtime.GOOS
	if sysType == "darwin" {
		// MacPro系统
		return getDarwinSerialNumber()
	}
	if sysType == "linux" {
		// LINUX系统
		return GetLinuxSerialNumber()
	}
	if sysType == "windows" {
		// windows系统
		return GetWindowsSerialNumber()
	}
	return ""
}

func GetWindowsSerialNumber() string {
	cmd := exec.Command("wmic", "baseboard", "get", "serialnumber") // Windows平台上使用WMIC命令获取主板序列号
	output, err := cmd.Output()
	if err != nil {
		return ""
	}
	// 从输出中提取主板序列号信息
	lines := strings.Split(string(output), "\n")
	for _, line := range lines[1:] {
		fields := strings.Fields(line)
		if len(fields) > 0 {
			return fields[0]
		}
	}

	return ""
}

func GetLinuxSerialNumber() string {
	cmd := exec.Command("dmidecode", "-s", "system-serial-number") // dmidecode命令用于读取系统信息
	output, err := cmd.Output()
	if err != nil {
		return ""
	}
	// 去除输出结果前后的空格并返回
	return strings.TrimSpace(string(output))
}

func getDarwinSerialNumber() string {
	cmd := exec.Command("system_profiler", "SPHardwareDataType")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return ""
	}
	re := regexp.MustCompile(`Serial Number (.+)`)
	matches := re.FindSubmatch(out.Bytes())
	if len(matches) < 2 {
		return ""
	}
	if strings.Contains(string(matches[0]), ":") {
		return strings.Trim(strings.Split(string(matches[0]), ":")[1], "")
	}

	return string(matches[0])
}

// func (c *SignController) GetKey() *web.JsonResult {
// 	// 创建私钥文件
// 	priFile, err := os.Create("private.key") // 也可以使用buffer, priBuf := bytes.NewBuffer(nil)
// 	if err != nil {
// 		panic(err)
// 	}
// 	defer priFile.Close()
// 	// 生成私钥到文件
// 	_ = openssl.RSAGenerateKey(1024, priFile)

// 	// 创建公钥文件
// 	pubFile, err := os.Create("public.key")
// 	if err != nil {
// 		panic(err)
// 	}
// 	defer priFile.Close()

// 	// 通过私钥生成公钥到文件
// 	priByte, _ := os.ReadFile("private.key")
// 	_ = openssl.RSAGeneratePublicKey(priByte, pubFile)
// 	return web.JsonData("")
// }
